SORTING VARIABLES
from Your Computer, May 1983

If you find that setting the parameters of a machine-code
routine is a bit of a drag, Stewart Nicholls' program could
give you the best of both worlds.


DO YOU GET tired of having to set parameters of a machine-code routine by
POKEing numerous addresses with the necessary values? If you do, the following
machine-code routine is the answer; it makes POKEing completely unnecessary.

The problem is avoided by setting up the values using BASIC variables and
then searching the variables area to find their value and storing them in the
spare bytes of the printer buffer.

To demonstrate the technique, I have attached the search program to a routine
which prints a message anywhere on the screen using characters of any height
or width, in any ink colour.

The x and y variables are used for the start of the message; since these are
the co-ordinates of the top-left corner of the first character of the message
the usual Spectrum plot positions apply. That is, 0,0 is the bottom left-hand
corner of the screen. For the height of the characters, h is used. Normal
height is 1, 22 will be full screen. Width of characters is w. Normal width
is 1, 32 is full screen for one character.

The string containing the message to be printed will be a$. The only
limitations on the message are that it can only contain characters from code
32 - space - to code 127 - copyright - and that the number of characters
multiplied by width of character should not exceed 32 with the PLOT position
at the left-hand edge of screen. If this is exceeded the program will not
crash but the message will wrap round the screen overprinting as it goes.

The program is fully mug-trapped and any wrong parameter of x and y will be
corrected before plotting the string. The machine code to find the variables
x, y, h, w and a$ is shown in listing 1.

If you read through Chapter 24 of the Sinclair Manual you will see that there
are six types of variable, namely: a number whose name is one letter - type
(i); a number whose name is two or more letters - type (ii); an array of
numbers - type (iii); For-Next loop control - type (iv); string variable -
type (v); an array of characters - type (vi).

Each of these variables is uniquely identified by its first byte
Variable type i   =  96d + (letter code - 96d)
Variable type ii  = 160d + (first letter code - 96d)
Variable type iii = 128d + (letter code - 96d)
Variable type iv  = 224d + (letter code - 96d)
Variable type v   =  64d + (letter code - 96d)
Variable type vi  = 192d + (letter code - 96d)
The variables we will be searching for will be types (i) and (v).

If we take, for example, the variable x which has a character code of 120,
then using the formula (i) given, its unique first byte is 120. So in order
to find variable x we must search through the variables for this unique code,
which will then point to the start of the x variable information. This is not
just a simple case of running through the variables one byte at a time using
a CPIR instruction, as this may find the code 120 held in a string or an
array of characters. So we must find the start of each variable, check if it
is the one we want, and if not, then jump to the start of the next variable.
This means that we must identify the type of variable found before we know
how far to jump to the next one. This is not as difficult a task as it might
first appear because of the way in which the Spectrum stores the variable
parameters.

With three of the variables, namely array of numbers, array of characters and
strings, the length of the variable is held in the two bytes following the
unique code byte. What is more helpful, bit 5 of the unique code is zero and
the remaining three variables have bit 5 set at one.

The length of the three remaining variables can be calculated by this method:
Type (i) length  = 6 bytes including unique code.
Type (ii) length = number of letters of variable + 5 bytes.
Type (iv) length = 19 bytes including unique code.
We now have the information to enable us to jump over each type of variable,
and the checks must be made in the following order: first, check if code is
120; if yes then return from routine; check bit 5 and if zero jump by
	value in next two bytes + 1: GOTO 1
Check bit 6 and if zero check following bytes for
	BIT 7= 1
that is the last character of the variable, then jump six bytes and GOTO 1.
Check bit 7 and if zero then jump 6 bytes and GOTO 1. Now it must be a
For/Next loop so jump 19 bytes and GOTO 1.

The mnemonics of the machine-code program to do this are shown in the FIND
subroutine. This uses address 23728 to hold the unique code of the variable
for which we are searching. The HL register holds the address being checked,
and the accumulator is loaded with the unique code from address 23728. The
subroutine only takes 46 bytes to check, jump and locate the start address of
any variable.

Now that we have found the start address of our variable x we must find its
value. You will see that the value for whole numbers is held in the third and
fourth bytes following the unique code. In our case we are only interested in
numbers from 0 to 255 for x. So we can ignore the high byte and store the low
byte, that is the third byte. This applies to all our variables x, y, h and w.

With this in mind a subroutine - Setup - can be assembled to move along three
bytes from the address held in HL and then store the value held in this
address in the printer buffer area of memory. So we now have the means of
finding and storing values of x, y, h and w in addresses 23296/7/8/9.

We can now move to the slightly more complicated string parameters. In our
case we need to find a$, unique code 65. The same FIND routine can be used to
find the start of the variable. Once found we then move to the next two bytes
to find the LEN of the string as follows:
	INC HL
	LD E,(HL)
	INC HL
	LD D,(HL)
This will put the length of the string into the DE register and, as we can
safely limit the length of the string to 255 characters we only need to store
the number in low byte:
	LD (23300),DE
The number in address 23301 will overwritten with the first character in the
string:
	PUSH DE  Get number of characters into BC
	POP BC
	INC HL   Set HL at start of characters
	DE,23301
	LDIR     Transfer information
The above will transfer the string characters from the variables to the
printer buffer starting at address 23301.

With this routine we now have x, y, h, w, length of string, and characters in
string stored consecutively in the printer buffer, and can go straight into
the routine to plot the string based on these parameters.

The plot routine finds the start of the eight bytes for each character in the
character set and, for each byte, a Rotate Left instruction is carried out
either plotting or unplotting depending whether Carry is set or not. Again
use is made of the two unused bytes in the Systems Variables 23728/9 to hold
and update the x,y plot positions for each character.

If the y plot position goes below zero, then it is reset to 175 to give a
wrap around effect and likewise if the x position goes above 255 it is reset
to zero. So now we have a machine-code program that can be called from BASIC
with no POKEs in sight - for example:
	10 LET x=0: LET y = 100: LET h = 8:
	   LET w=4: LET a$ = "Finished": INK 6:
	   RANDOMIZE USR 32393
This saves 13 POKEs including LEN a$. Listing 1 shows the BASIC program to
set up the machine code above RAMTOP. On a 16K Spectrum this is immediately
before the user-defined graphics and occupies 265 bytes. Once the
machine-code is entered the BASIC can be NEWed and the code saved in the
usual way.

There are a couple of points to watch with this program. First, ensure that
all the variables are defined before a call is made to the routine as failure
to find a variable will crash the program. Second, do not use x, y, h, or w
as a control variable for a FOR-NEXT loop because the Spectrum will then
delete the simple variable and use the FOR-NEXT loop to hold further values
of x, y, h and w. This can be demonstrated by the simple BASIC program:
	10 FOR a=1 TO 10
	20 PRINT a;
	30 NEXT a
	40 LET a=3
	50 NEXT a
This program will produce a continuous loop resetting a to 3 and jumping back
into the FOR-NEXT loop showing that the variable - a - in line 40 is part of
the FOR-NEXT loop control variable. If this was not the case the Spectrum
would give an error report:
	1 NEXT without FOR, 50:1

You can have a small BASIC subroutine to work out the width of characters and
the start plot position to give information printed centrally on any line;
such a program is given in listing 2 with sample printout.

We can now also tackle a machine-code program to print out all the variables
used after a program has been RUN. Note that this may not be all the
variables in the listing as when a program is RUN certain subroutines may not
have been called and so variables held in those routines will not have been
placed into the variables area.

This program - listing 3 - could be of use in debugging BASIC programs.
Listing 3 is in the form of a machine-code dump which should be held in DATA
statements as listing 1, and POKEd into a memory position of your choice.
There are no Jump or Call commands to within the program. I find it handy to
have two versions of the program, one stored above RAMTOP and another in a
line 1 REM statement containing 311 zeros and called using:
	RANDOMIZE USR 23760
The machine-code is then held in the BASIC area of RAM and can be MERGEd with
any program - as long as the program does not have a line 1, which will be
overwritten. One point to note when SAVEing the BASIC REM statement on tape is
to use CLEAR to erase all variables used in the machine-code loader,
otherwise these will be saved and MERGEd along with the REM statement. This
is good practice on any program which does not require the variables to be
saved.

Listing 4 is a demonstration program setting up variables, and shows a screen
copy of the machine-code output which lists these variables under their
variable types. Notice how the Spectrum converts all variables to lower case
and also that variables a, h, l, o, w, x and z have been listed under the
FOR-NEXT loop only and not under number.

Listing 5 shows the machine-code mnemonics for assembly into a REM statement,
for anyone who wishes to check through the program to see how it works. The
first section from address 5CD0 to 5CE5 sets up the BORDER, PAPER and INK
colours; from 5CE6 to 5D46 prints the heading. The remainder of the program
checks the variables one by one finding the type of variable, locating the
next PRINT position in that variables column, updating the PRINT position and
printing the variable.

The section from 5D94 to 5DBE is interesting in that it checks for a column
reaching the bottom of the screen and if it does then waits for a key press
before scrolling 21 lines, leaving the heading on the screen and printing the
next variable in position. A return to BASIC is only made when all the
variables have been listed.
